/**
 ********************************************************************
 * @author roocket
 * @file    uart.c
 * @version V0.0.0
 * @date    2024/3/22
 * @brief   This file is to indicate uart functions. 
 * @attention Code file rules:
 * rule: file encoding use UTF8;
 * rule: max line length 120 characters;
 * rule: line separator \r\n;
 * rule: use clion auto code format tool.
 */
#include "uart.h"
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>


unsigned int uart_get_baud_rate(int id)
{
    unsigned int baud;
    switch (id)
    {
        case 0:
        baud = B0;
        break;
        case 50:
        baud = B50;
        break;
        case 75:
        baud = B75;
        break;
        case 110:
        baud = B110;
        break;
        case 134:
        baud = B134;
        break;
        case 150:
        baud = B150;
        break;
        case 200:
        baud = B200;
        break;
        case 300:
        baud = B300;
        break;
        case 600:
        baud = B600;
        break;
        case 1200:
        baud = B1200;
        break;
        case 1800:
        baud = B1800;
        break;
        case 2400:
        baud = B2400;
        break;
        case 9600:
        baud = B9600;
        break;
        case 19200:
        baud = B19200;
        break;
        case 38400:
        baud = B38400;
        break;
        case 57600:
        baud = B57600;
        break;
        case 115200:
        baud = B115200;
        break;
        case 460800:
        baud = B460800;
        break;
        case 921600:
        baud = B921600;
        break;
        case 1000000:
        baud = B1000000;
        break;
        default:
        printf("input error: baud rate not correct");
        return -1;
    }
    return baud;
}

int uart_init(void **port, char *name, int baud)
{
    int fd;
    int ret;
    struct termios options;

    fd = open(name, O_RDWR | O_NDELAY | O_NOCTTY);

    if (fd == -1) {
        printf("%s: open error\n", name);
        return -1;
    }
    ret = tcgetattr(fd, &options);
    if (-1 == ret)
        return -1;

    options.c_cflag &= ~CSIZE; 
    options.c_cflag |= CS8;    
    options.c_cflag &= ~PARENB;
    options.c_cflag &= ~CSTOPB;

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    cfsetispeed(&options, baud);
    cfsetospeed(&options, baud);
    options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
    ret = tcsetattr(fd, TCSANOW, &options);
    if (-1 == ret)
        return -1;

    *port = (void *)fd;
    return ret;
}

static int _select_fd_data(int fd, int timeout_s, int timeout_us)
{
    fd_set rfds;
    struct timeval timeval;
    int ret;
    int max_fd;

    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

    /* set timeout */
    timeval.tv_sec = timeout_s;
    timeval.tv_usec = timeout_us;

    max_fd = fd;
    ret = select(max_fd + 1, &rfds, NULL, NULL, &timeval);

    if (-1 == ret)
        perror("select");
    else if (ret) {
        printf("available data\n");
        ret = 0;
    } else {
        printf("no data within %d sec %d usec \n", timeout_s, timeout_us);
        ret = -1;
    }
    return ret;
}

int uart_send_data(void *port, void *__buf, int len)
{
    int ret = -1;
    int fd = (int)port;

    /* select fd */
    ret = _select_fd_data(fd, 2, 0);
    if (-1 == ret) {
        perror("select failed\n");
        return -1;
    }

    /* write data */
    ret = write(fd, __buf, len);
    if (-1 == ret) {
        perror("write\n");
        return -1;
    }

    return 0;
}

int uart_recv_data(void *port, void *buf, int *len)
{
    int ret = -1;
    int fd = (int)port;

    /* select fd */
    ret = _select_fd_data(fd, 2, 0);
    if (-1 == ret) {
        perror("select failed\n");
        return -1;
    }

    /* write data */
    ret = read(fd, buf, 1024);
    if (-1 == ret) {
        perror("read\n");
        return -1;
    }
    /* set length for buffer */
    *len = ret;
    return 0;
}
